// WaveFile.cpp: implementation of the CWaveFile class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "TDPsola.h"
#include "WaveFile.h"
#include "Winsock2.h"
#include "Math.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

#include "MyConstants.h"
int				CWaveFile::m_nFileOpenState = WF_NOERROR;
WAVEHDR			CWaveFile::m_whdr;
HWAVEOUT		CWaveFile::m_hwo = NULL;
BOOL			CWaveFile::m_hPlay = FALSE;
BOOL			CWaveFile::m_bOnStopFunction = FALSE;
WAVEFORMATEX	CWaveFile::m_wfx;
char*			CWaveFile::m_cTempBuffer = NULL;

CWaveFile::CWaveFile()
{
	m_cContent = new char [44];
	this->m_bDirty = FALSE;

	this->m_uSampleRate = 0;
	this->m_wBitsPerSample = 0;
	this->m_wNumberChannels = 0;
	this->m_dwFileSize = 0;
	this->m_dwDataSize = 0;
}

CWaveFile::~CWaveFile()
{
	delete[] m_cContent;
}

BOOL CWaveFile::Attach(LPCTSTR lpszFileName)
{
	if(this->m_bDirty) this->Close();
	CFile fWaveFile;
	if(!fWaveFile.Open(lpszFileName,CFile::modeRead))
	{
		this->m_nFileOpenState = WF_CANTOPEN;
		return FALSE;
	}
	this->m_dwFileSize = fWaveFile.SeekToEnd();
	if( this->m_dwFileSize < 44)
	{
		this->m_nFileOpenState = WF_TOOSHORT;
		return FALSE;
	}

	fWaveFile.SeekToBegin();
	fWaveFile.Read(m_cContent,44);

	if((m_cContent[0]!='R')||(m_cContent[1]!='I')||(m_cContent[2]!='F')||(m_cContent[3]!='F')
		||(m_cContent[8]!='W')||(m_cContent[9]!='A')||(m_cContent[10]!='V')||(m_cContent[11]!='E')
		||(m_cContent[12]!='f')||(m_cContent[13]!='m')||(m_cContent[14]!='t')||(m_cContent[15]!=' ')
		||(m_cContent[36]!='d')||(m_cContent[37]!='a')||(m_cContent[38]!='t')||(m_cContent[39]!='a'))
	{
		this->m_nFileOpenState = WF_NOTPCMFILE;
		return FALSE;
	}
	
	if(( *(UINT *)(m_cContent+4) != this->m_dwFileSize - 8)
		||(*(UINT*)(m_cContent + 16)!= 16)||(*(WORD*)(m_cContent+20)!=1)
		||(*(UINT*)(m_cContent+40)) > (this->m_dwFileSize - 44))
		//////SizeOfData =NumSamples * NumChannels * BitsPerSample/8                             
	{
		this->m_nFileOpenState = WF_INFONOTMATCH;
		return FALSE;
	}
	if((*(UINT*)(m_cContent+40))==0)
	{
		this->m_nFileOpenState = WF_NODATAINDATACHUNK;
		return FALSE;
	}

	this->m_wBitsPerSample = *(WORD*)(m_cContent + 34);
	if((m_wBitsPerSample != 8)&&(m_wBitsPerSample != 16)&&(m_wBitsPerSample != 32))
	{
		this->m_nFileOpenState = WF_NOTSUPPORTED;
		return FALSE;
	}

	this->m_wNumberChannels = *(WORD*)(m_cContent + 22);
	this->m_uSampleRate = *(UINT*)(m_cContent + 24);
	this->m_dwDataSize = *(UINT*)(m_cContent+40);
	delete[] m_cContent;
	m_cContent = new char [this->m_dwDataSize];
	fWaveFile.Read(m_cContent,this->m_dwDataSize);
	this->UnsignedToSigned(m_cContent,this->m_dwDataSize);
	m_csTitle = fWaveFile.GetFileName();
	m_csFullPath = fWaveFile.GetFilePath();
	fWaveFile.Close();
	this->m_bDirty = TRUE;
	return TRUE;
}

/////////Ham nay dung de lay chuoi bao loi gan nhat
CString CWaveFile::GetLastError(int *nErrorCode)
{
	if(nErrorCode) *nErrorCode = m_nFileOpenState;
	switch(m_nFileOpenState)
	{
		case WF_ERROR :					return "Error non determinal";
		case WF_CANTOPEN :				return "Can not open this file";
		case WF_TOOSHORT :				return "File is too short";
		case WF_NOTPCMFILE:				return "This is not PCM file";
		case WF_INFONOTMATCH :			return "Some informations in this file not match";
		case WF_NOWAVEFILEATTACHED :	return "No wave file attached";
		case WF_ERRORINSEPARATING :		return "Error in separating";
		case WF_NODATASEPARATED	:		return "No data separated";
		case WF_NOTSUPPORTED	:		return "Support only the PCM with 8,16 or 32 bits per sample";
		case WF_NODATAINDATACHUNK	:	return "The data chunk contain no data";
		case WF_CANNOTOPENOUTPUTDEVICE:	return "Cannot open output device";
		case WF_CANNOTPREPAREHDR :		return "Cannot prepare Header";
		case WF_CANNOTWRITEOUTPUTDEVICE:return "Cannot write ouput device";
		case WF_ANOTHERISPLAYING	:	return "There is another file is playing";
		case WF_BITSPERSAMPLENOTMATCH :	return "Bits per sample not match";
		case WF_NUMBERCHANNELSNOTMATCH:	return "Number channels not match";
		case WF_SAMPLERATENOTMATCH:		return "SampleRate not match";
	}
	return "No Error";
}

DWORD CWaveFile::GetFileSize()
{
	if(!this->m_bDirty) return 0;
	return this->m_dwFileSize;
}

WORD CWaveFile::GetNumberChannels()
{
	if(!this->m_bDirty) return 0;
	return this->m_wNumberChannels;
}

UINT CWaveFile::GetSampleRate()
{
	if(!this->m_bDirty) return 0;
	return this->m_uSampleRate;
}

WORD CWaveFile::GetBitsPerSample()
{
	if(!this->m_bDirty) return 0;
	return this->m_wBitsPerSample;
}

DWORD CWaveFile::GetDataSize()
{
	if(!this->m_bDirty) return 0;
	return (this->m_wNumberChannels == 1) ? this->m_dwDataSize : (this->m_dwDataSize/2);
}

CString CWaveFile::GetFileName()
{
	if(!this->m_bDirty) return "";
	return this->m_csTitle;
}

CString CWaveFile::GetFilePath()
{
	if(!this->m_bDirty) return "";
	return this->m_csFullPath;
}

/////////Ham nay dung de lay du lieu ra hai kenh
/////////Count la so byte du lieu o moi kenh
/////////Neu so kenh == 1 thi se khong co du lieu o kenh 2
/////////Ham nay tra ve du lieu o dang co dau
BOOL CWaveFile::SeparateData(LPSTR m_cSignalChannel1,LPSTR m_cSignalChannel2,DWORD dwCount)
{
	if(!this->m_bDirty){
		this->m_nFileOpenState = WF_NOWAVEFILEATTACHED; 
		return FALSE;
	}

	if(this->m_wNumberChannels == 1){
		memcpy(m_cSignalChannel1,this->m_cContent,dwCount);
		return TRUE;
	}

	if(this->m_wNumberChannels == 2){
		DWORD j = 0;
		DWORD i;
		switch(this->m_wBitsPerSample)
		{
			case 8:
				if(this->m_dwDataSize < 2 ){
					this->m_nFileOpenState = WF_NODATASEPARATED;
					return FALSE;
				}
				for(i = 0;(i <= this->m_dwDataSize - 2)&&(j<dwCount);i+=2)
				{
					m_cSignalChannel1[j] = this->m_cContent[i];
					m_cSignalChannel2[j] = this->m_cContent[i+1];
					j++;
				}
				return TRUE;
			case 16: 
				if(this->m_dwDataSize < 4){
					this->m_nFileOpenState = WF_NODATASEPARATED;
					return FALSE;
				}
				if(dwCount < 2){
					memcpy(m_cSignalChannel1,this->m_cContent,dwCount);
					memcpy(m_cSignalChannel2,this->m_cContent+2,dwCount);
					return TRUE;
				}

				j = 0;
				for(i = 0;(i<=this->m_dwDataSize-4)&&(j<=dwCount-2);i+=4){
					*(WORD*)(m_cSignalChannel1+j) = *((WORD*)(m_cContent+i));
					*(WORD*)(m_cSignalChannel2+j) = *((WORD*)(m_cContent+i+2));
					j+=2;
				}
				return TRUE;
			case 32:
				if(this->m_dwDataSize < 8){
					this->m_nFileOpenState = WF_NODATASEPARATED;
					return FALSE;
				}
				if(dwCount < 4){
					memcpy(m_cSignalChannel1,this->m_cContent,dwCount);
					memcpy(m_cSignalChannel2,this->m_cContent+4,dwCount);
					return TRUE;
				}
				j = 0;
				for(i = 0;(i<=this->m_dwDataSize-8)&&(j<=dwCount-4);i+=8)
				{
					*(UINT*)(m_cSignalChannel1+j) = *((UINT*)(m_cContent+i));
					*(UINT*)(m_cSignalChannel2+j) = *((UINT*)(m_cContent+i+4));
					j+=4;
				}
				return TRUE;
		}
	}
	this->m_nFileOpenState = WF_ERRORINSEPARATING;
	return FALSE;
}

void CWaveFile::Close()
{
	delete this->m_cContent;
	this->m_cContent = new char [44];
	this->m_nFileOpenState = WF_NOERROR;
	this->m_bDirty = FALSE;	

	this->m_csFullPath.Empty();
	this->m_csTitle.Empty();
	this->m_uSampleRate = 0;
	this->m_wBitsPerSample = 0;
	this->m_wNumberChannels = 0;
	this->m_dwFileSize = 0;
	this->m_dwDataSize = 0;
}

void CWaveFile::UnsignedToSigned(char *m_cSignal, DWORD dwCount)
{
	if(this->m_wBitsPerSample!=8) return;
	for(DWORD i = 0;i<dwCount;i++)
		m_cSignal[i] = (BYTE)m_cSignal[i] - 128;
}

/////////Co the dung ham nay de ghi ra File wav voi mot chuoi du lieu chua ca 2 kenh 
////////(khi do lpszChannel2 phai la NULL
/////////hoac 2 chuoi du lieu cho 2 kenh
/////////Du lieu vao phai la du lieu dang co dau (Ke ca 8 bit)
BOOL CWaveFile::CreateWaveFile(	LPCTSTR	lpszFileName,UINT uSampleRate,
								WORD	wBitsPerSample,
								WORD	wNumberChannels,DWORD dwDataSize,
								LPSTR	lpChannel1,LPSTR lpChannel2)
								//LPCTSTR lpszFileName, UINT uSampleRate, WORD wBitsPerSample, WORD	wNumberChannels,DWORD dwDataSize, LPSTR	lpChannel1,LPSTR lpChannel2
{
	ASSERT((wNumberChannels==2)||(wNumberChannels==1));
	if((wBitsPerSample != 8)&&(wBitsPerSample != 16)&&(wBitsPerSample != 32))
	{
		m_nFileOpenState = WF_NOTSUPPORTED;
		return FALSE;
	}
	
	CFile fWaveFile;
	if(!fWaveFile.Open(lpszFileName,CFile::modeWrite|CFile::modeCreate))
	{
		m_nFileOpenState = WF_CANTOPEN;
		return FALSE;
	}

	char *cHeader = new char[44];
	*(WORD*)(cHeader+22) = wNumberChannels;
	*(UINT*)(cHeader+24) = uSampleRate;
	*(UINT*)(cHeader+40) = dwDataSize;

	cHeader[0] = 'R';
	cHeader[1] = 'I';
	cHeader[2] = 'F';
	cHeader[3] = 'F';

	*(UINT*)(cHeader+4) = dwDataSize * wNumberChannels + 44 - 8;

	cHeader[8] = 'W';
	cHeader[9] = 'A';
	cHeader[10] = 'V';
	cHeader[11] = 'E';

	cHeader[12] = 'f';
	cHeader[13] = 'm';
	cHeader[14] = 't';
	cHeader[15] = ' ';

	*(UINT*)(cHeader+16) = 16;
	*(WORD*)(cHeader+20) = 1;
	*(UINT*)(cHeader+28) = uSampleRate * wNumberChannels * wBitsPerSample/8;
	*(WORD*)(cHeader+32) = wNumberChannels * wBitsPerSample/8;

	*(WORD*)(cHeader+34) = wBitsPerSample;

	cHeader[36] = 'd';
	cHeader[37] = 'a';
	cHeader[38] = 't';
	cHeader[39] = 'a';

	*(UINT*)(cHeader+40) = dwDataSize * wNumberChannels;
	fWaveFile.Write(cHeader,44);
	delete[] cHeader;
	DWORD i,j;
	if(wNumberChannels==1)
	{
		char *cData = new char[dwDataSize];
		switch(wBitsPerSample)
		{
			case 8: for(i = 0;i<dwDataSize;i+=1){
						cData[i] = *(lpChannel1+i)+128;
					}
					fWaveFile.Write(cData,dwDataSize);
					break;
			case 16:
			case 32:fWaveFile.Write(lpChannel1,dwDataSize);
		}
		delete[] cData;
	}
	else
	{
		char *cData = new char[2 * dwDataSize];
		switch(wBitsPerSample)
		{
			case 8: j = 0;
					for(i = 0;i<dwDataSize;i+=1){
						cData[j] = *(lpChannel1+i)+128;
						cData[j+1] = *(lpChannel2+i)+128;
						j+=2;
					}
					break;
			case 16:j = 0;
					for( i = 0; i+2<= dwDataSize; i+=2 ){
						*( short * )( cData + j ) = *((short*)(lpChannel1 + i ));
						*( short * )( cData + j + 2 ) = *((short*)(lpChannel2 + i ));
						j+=4;
					}
					break;
			case 32:
					break;
		}
		fWaveFile.Write( cData,2*dwDataSize);
		delete[] cData;
	}
	fWaveFile.Close();
	return TRUE;
}

void CALLBACK waveOutProc(HWAVEOUT hwo,UINT uMsg,DWORD dwInstance,  
		DWORD dwParam1, DWORD dwParam2)
{
	if( uMsg == WOM_DONE)
	{
		//access violation ??
		//MMRESULT Ret= waveOutUnprepareHeader(hwo,(LPWAVEHDR)dwParam1,sizeof(WAVEHDR));
		//ASSERT(Ret==MMSYSERR_NOERROR); 
		CWaveFile::m_hPlay = FALSE;
		//waveOutClose(hwo);
		delete[] CWaveFile::m_cTempBuffer;
		MessageBox(::AfxGetMainWnd()->m_hWnd,"kdjf",0,0);
	}
}

/////dwFrom va dwLen tinh theo mau
BOOL CWaveFile::PlayBuffer(WAVEFORMATEX wofx,LPSTR lpSignal1,DWORD dwFrom,DWORD dwLen)
{	
	/* Stop();
	m_wfx = wofx;
	//if(waveOutOpen((LPHWAVEOUT)&m_hwo,WAVE_MAPPER, 
	//	&m_wfx,(DWORD)waveOutProc,0L,CALLBACK_WINDOW)) 
	if(waveOutOpen((LPHWAVEOUT)&m_hwo,WAVE_MAPPER, 
		&m_wfx,(DWORD)(::AfxGetMainWnd()->m_hWnd),0L,CALLBACK_WINDOW)) 
    { 
         m_nFileOpenState = WF_CANNOTOPENOUTPUTDEVICE;
         return FALSE; 
    } 

	m_cTempBuffer = new char [dwLen * m_wfx.nBlockAlign + 10];
	memcpy(m_cTempBuffer,lpSignal1 + dwFrom * m_wfx.nBlockAlign,dwLen * m_wfx.nBlockAlign);
  
 	//Dat cac thuoc tinh cho WAVEHDR va chuan bi xuat ra thiet bi
    m_whdr.lpData = m_cTempBuffer;
    m_whdr.dwBufferLength = dwLen* m_wfx.nBlockAlign;
    m_whdr.dwFlags = 0L ;
	m_whdr.dwLoops = 0L;
         
 	if(waveOutPrepareHeader( m_hwo,&m_whdr,sizeof(WAVEHDR) ) )
    {
		m_nFileOpenState = WF_CANNOTPREPAREHDR;
		delete[] m_cTempBuffer;
		m_cTempBuffer = NULL;
        return FALSE;
	}
 
    //Gui block du lieu ra thiet bi xuat
    if (waveOutWrite( m_hwo,&m_whdr,sizeof(WAVEHDR)) )
    {
        //waveOutUnprepareHeader(m_hwo,&whdr,sizeof(WAVEHDR));
		m_nFileOpenState = WF_CANNOTWRITEOUTPUTDEVICE;
		delete[] m_cTempBuffer;
		m_cTempBuffer = NULL;
        return FALSE;
    }
	m_hPlay = TRUE;*/
	return TRUE; //thaodv 12052011
}

int CWaveFile::Stop()
{
	/* if(m_hPlay){
		waveOutReset(m_hwo);
	}
	if(!m_cTempBuffer){
		delete[] m_cTempBuffer;
		m_cTempBuffer = NULL;
	}
	m_hPlay = FALSE;
	return waveOutClose(m_hwo); */
	return 0; //thaodv 12052011
}

BOOL CWaveFile::PlayFile(LPCTSTR lpszFileName)
{
	/* PlaySound( NULL,NULL, SND_ASYNC | SND_NODEFAULT );
	OFSTRUCT stOF;
	if((!lpszFileName) ||(!OpenFile(lpszFileName,&stOF, OF_EXIST)))
	{
		m_nFileOpenState = WF_CANTOPEN;
		return FALSE;
	}
	return PlaySound( lpszFileName,NULL, SND_ASYNC | SND_NODEFAULT );	 */
	return TRUE;//thaodv 12052011
}

BOOL CWaveFile::AppendToWaveFile(APPENDWAVESTRUCT &stAppWave)
{
	CFile fWaveFile;
	if(!fWaveFile.Open(stAppWave.lpszFileName,CFile::modeReadWrite))
	{
		m_nFileOpenState = WF_CANTOPEN;
		return FALSE;
	}
	DWORD m_dwFileSize = fWaveFile.SeekToEnd();
	if(m_dwFileSize < 44)
	{
		m_nFileOpenState = WF_TOOSHORT;
		return FALSE;
	}

	char *m_cContent = new char [44];
	fWaveFile.SeekToBegin();
	fWaveFile.Read(m_cContent,44);

	if((m_cContent[0]!='R')||(m_cContent[1]!='I')||(m_cContent[2]!='F')||(m_cContent[3]!='F')
		||(m_cContent[8]!='W')||(m_cContent[9]!='A')||(m_cContent[10]!='V')||(m_cContent[11]!='E')
		||(m_cContent[12]!='f')||(m_cContent[13]!='m')||(m_cContent[14]!='t')||(m_cContent[15]!=' ')
		||(m_cContent[36]!='d')||(m_cContent[37]!='a')||(m_cContent[38]!='t')||(m_cContent[39]!='a'))
	{
		m_nFileOpenState = WF_NOTPCMFILE;
		delete m_cContent;
		return FALSE;
	}
	
	if(( *(UINT *)(m_cContent+4) != m_dwFileSize - 8)
		||(*(UINT*)(m_cContent + 16)!= 16)||(*(WORD*)(m_cContent+20)!=1)
		||(*(UINT*)(m_cContent+40)) > (m_dwFileSize - 44))
		//////SizeOfData =NumSamples * NumChannels * BitsPerSample/8                             
	{
		m_nFileOpenState = WF_INFONOTMATCH;
		delete m_cContent;
		return FALSE;
	}

	WORD m_wBitsPerSample = *(WORD*)(m_cContent + 34);
	if((m_wBitsPerSample != 8)&&(m_wBitsPerSample != 16)&&(m_wBitsPerSample != 32))
	{
		m_nFileOpenState = WF_NOTSUPPORTED;
		delete m_cContent;
		return FALSE;
	}

	if(stAppWave.wBitsPerSample != m_wBitsPerSample)
	{
		m_nFileOpenState = WF_BITSPERSAMPLENOTMATCH;
		delete m_cContent;
		return FALSE;
	}

	if(stAppWave.uSampleRate != *(UINT*)(m_cContent + 24))
	{
		m_nFileOpenState = WF_SAMPLERATENOTMATCH;
		delete m_cContent;
		return FALSE;
	}

	if(stAppWave.wNumberChannels != *(WORD*)(m_cContent + 22))
	{
		m_nFileOpenState = WF_NUMBERCHANNELSNOTMATCH;
		delete m_cContent;
		return FALSE;
	}

	*(UINT *)(m_cContent+4) += stAppWave.dwLen;	
	*(UINT*)(m_cContent+40) += stAppWave.dwLen;
	fWaveFile.SeekToBegin();
	fWaveFile.Write(m_cContent,44);
	fWaveFile.SeekToEnd();
	fWaveFile.Write(stAppWave.lpSignal,stAppWave.dwLen);

	delete m_cContent;
	fWaveFile.Close();
	return TRUE;
}

BOOL CWaveFile::CreateAuFile(AUDIOHEADER &hdr)
{
	/* CFile f;
	if(!f.Open("D:\\Thu.au",CFile::modeWrite | CFile::modeCreate ))
	{
		m_nFileOpenState = WF_CANTOPEN;
		return FALSE;
	}
	
	char *temp1 = new char [48];
	memset(temp1,0,48);
	temp1[0] = '.';
	temp1[1] = 's';
	temp1[2] = 'n';
	temp1[3] = 'd';
	*(DWORD*)(temp1+4) = (DWORD)htonl(48);
	*(DWORD *)(temp1+8) = (DWORD)htonl(hdr.data_size);
	*(DWORD *)(temp1+12) = (DWORD)htonl(1);
	*(DWORD *)(temp1+16) = (DWORD)htonl(8000);
	*(DWORD *)(temp1+20) = (DWORD)htonl(1);
	DWORD t = (DWORD)htonl(1);

	f.Write(temp1,48);
	f.Write(hdr.lpData,hdr.data_size);
	f.Close();
	delete[] temp1;
	return TRUE; */
	return TRUE;//thaodv 12052011
}

BOOL CWaveFile::Convert16To8MuLaw(char *lpSrc, char *lpDst, DWORD dwDataSize)
{
	DWORD j = 0;
	DWORD exp = 0;
	short temp;
	memset(lpDst,0,dwDataSize/4);
	for(DWORD i = 0; i < dwDataSize; i+=4)
	{
		*(lpDst + j) = ((*(short *)(lpSrc + i)) & 0x8000)>>8;
		temp = abs((*(short *)(lpSrc + i)))|(short)(33<<2);
		exp = 7;
		while(!(temp & 0x4000))
		{
			temp = temp << 1;
			exp--;
		}
		*(lpDst + j)  |= (char)((temp & 0xBF00)>>10);
		*(lpDst + j) |= exp<<4;
		*(lpDst + j) = (*(lpDst + j))^0x7f;
		j++;
	}
	return TRUE;
}

